/**
 * @Deprecated!
 * @用react-imported-component 来替换 JSResource.js功能。
 * 目前只能使用JSResource.js，还没其它可行的办法或者适用第三方包。
 * 自定义 路由器，使用到的！
 * A cache of resources to avoid loading the same module twice. This is important
 * because Webpack dynamic imports only expose an asynchronous API for loading
 * modules, so to be able to access already-loaded modules synchronously we
 * must have stored the previous result somewhere.
 * 客户端的 内存缓存
 * 这个和浏览器自己的 什么关系, =双头保存,重复了吗？
 */
const resourceMap = new Map();

/**
 * A generic resource: given some method to asynchronously load a value - the loader()
 * argument - it allows accessing the state of the resource.
 * 资源装载器
 *
 *对方需要的是TS类型 JSResourceReference; 伪装成, 先尝试extends JSResourceReference
 */
class Resource {
  constructor(loader) {
    this._error = null;
    this._loader = loader;
    this._promise = null;
    this._result = null;
  }

  /**
   * Loads the resource if necessary.
   */
  load() {
    let promise = this._promise;
    if (promise == null) {
      promise = this._loader()
        .then(result => {
          if (result.default) {
            result = result.default;
          }
          this._result = result;
          return result;
        })
        .catch(error => {
          this._error = error;
          throw error;
        });
      this._promise = promise;
    }
    return promise;
  }

  /**
   * Returns the result, if available. This can be useful to check if the value
   * is resolved yet.
   */
  get() {
    if (this._result != null) {
      return this._result;
    }
  }

  /**
   * This is the key method for integrating with React Suspense. Read will:
   * - "Suspend" if the resource is still pending (currently implemented as
   *   throwing a Promise, though this is subject to change in future
   *   versions of React)
   * - Throw an error if the resource failed to load.
   * - Return the data of the resource if available.
   */
  read() {
    if (this._result != null) {
      return this._result;
    } else if (this._error != null) {
      throw this._error;
    } else {
      throw this._promise;
    }
  }
}

/**
 * A helper method to create a resource, intended for dynamically loading code.
 *
 * Example:
 * ```
 *    // Before rendering, ie in an event handler:
 *    const resource = JSResource('Foo', () => import('./Foo.js));
 *    resource.load();
 *
 *    // in a React component:
 *    const Foo = resource.read();
 *    return <Foo ... />;
 * ```
 * 【小心问题】注意！moduleId 整个SPA的内部唯一性。
 *
 * @param {*} moduleId A globally unique identifier for the resource used for caching
 * @param {*} loader A method to load the resource's data if necessary
 * SuspenseImage/图片组件： 也会使用[ src ],URI作为Map关键字的。
 * 避免Map.Key[] 名字上的冲突。
 * 【注意！】JSResource(moduleId, loader)运行若是前面已经有了moduleId对应资源，后面就不会再次修改了，同名字冲突！
 */
export default function JSResource(moduleId, loader) {
  let resource = resourceMap.get(moduleId);
  if (resource == null) {
    resource = new Resource(loader);
    resourceMap.set(moduleId, resource);
  }
  return resource;
}

